`timescale 1ns / 1ps

`include "data_width.vh"

module edge_process #(parameter
    EDGE_PIPE_NUM       = `EDGE_PIPE_NUM,
    VERTEX_PIPE_NUM     = `VERTEX_PIPE_NUM,
    VERTEX_BRAM_DWIDTH  = `VERTEX_BRAM_DWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH,
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH,
    TOT_ACC_ID_WIDTH    = `TOT_ACC_ID_WIDTH,
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    DEGREE_DWIDTH       = `DEGREE_DWIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]      front_src_p,
        input [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]           front_src_degree,
        input                                                   front_src_p_valid,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]                     front_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]                        front_tot_acc_id,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_data_valid,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]     front_src_p_mask_r,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,

        output                                                  rst,
        output [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]     src_p,
        output [TOT_ACC_ID_WIDTH - 1 : 0]                       tot_acc_id,
        output                                                  src_p_valid,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        dst_id,
        output [VERTEX_MASK_WIDTH * VERTEX_PIPE_NUM - 1 : 0]    src_p_mask_r,
        output [VERTEX_PIPE_NUM - 1 : 0]                        dst_data_valid);

    edge_process_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    edge_process_edge E1 (
        .clk                (clk),
        .rst                (front_rst),
        .front_src_p        (front_src_p),
        .front_src_degree   (front_src_degree),
        .front_src_p_mask   (front_src_p_mask),
        .front_tot_acc_id   (front_tot_acc_id),
        .front_src_p_valid  (front_src_p_valid),

        .src_p              (src_p),
        .tot_acc_id         (tot_acc_id),
        .src_p_valid        (src_p_valid));
    
    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M13_BLOCK_1
            edge_process_vertex_single V (
                .clk                    (clk),
                .rst                    (front_rst),
                .front_data_valid       (front_data_valid[i]),
                .front_dst_id           (front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src_p_mask_r     (front_src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .front_dst_data_valid   (front_dst_data_valid[i]),

                .dst_id                 (dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .src_p_mask_r           (src_p_mask_r[(i + 1) * VERTEX_MASK_WIDTH - 1 : i * VERTEX_MASK_WIDTH]),
                .dst_data_valid         (dst_data_valid[i]));
        end
    endgenerate

endmodule

module edge_process_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);
    
    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module edge_process_edge #(parameter
    EDGE_PIPE_NUM       = `EDGE_PIPE_NUM,
    VERTEX_BRAM_DWIDTH  = `VERTEX_BRAM_DWIDTH,
    TOT_EDGE_MASK_WIDTH = `TOT_EDGE_MASK_WIDTH,
    TOT_ACC_ID_WIDTH    = `TOT_ACC_ID_WIDTH,
    MAX_SRC_P           = `MAX_SRC_P,
    DEGREE_DWIDTH       = `DEGREE_DWIDTH,
    DIV_DELAY           = `DIV_DELAY
    ) (
        input                                                   clk,
        input                                                   rst,
        input [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]      front_src_p,
        input [DEGREE_DWIDTH * EDGE_PIPE_NUM - 1 : 0]           front_src_degree,
        input [TOT_EDGE_MASK_WIDTH - 1 : 0]                     front_src_p_mask,
        input [TOT_ACC_ID_WIDTH - 1 : 0]                        front_tot_acc_id,
        input                                                   front_src_p_valid,

        output [VERTEX_BRAM_DWIDTH * EDGE_PIPE_NUM - 1 : 0]     src_p,
        output [TOT_ACC_ID_WIDTH - 1 : 0]                       tot_acc_id,
        output                                                  src_p_valid);

    reg [TOT_ACC_ID_WIDTH - 1 : 0] tot_acc_id_buffer [0 : DIV_DELAY - 1];
    reg [DIV_DELAY - 1 : 0] src_p_valid_buffer;

    generate
        genvar i;
        for (i = 0; i < EDGE_PIPE_NUM; i = i + 1) begin : M13_BLOCK_2
            floating_point_divider FPD1 (
                .aclk               (clk),
                .s_axis_a_tvalid    (front_src_p_mask[i]),
                .s_axis_a_tdata     (front_src_p[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]),
                .s_axis_b_tvalid    (front_src_p_mask[i]),
                .s_axis_b_tdata     ({front_src_degree[i * DEGREE_DWIDTH + 31 : i * DEGREE_DWIDTH + 30], front_src_degree[i * DEGREE_DWIDTH + 26 : i * DEGREE_DWIDTH + 23], front_src_degree[i * DEGREE_DWIDTH + 22 : i * DEGREE_DWIDTH + 13]}),

                .m_axis_result_tdata(src_p[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]));
        end
        for (i = 1; i < DIV_DELAY; i = i + 1) begin : M13_BLOCK_3
            always @ (posedge clk) begin
                if (rst) begin
                    tot_acc_id_buffer[i] <= 0;
                    src_p_valid_buffer[i] <= 0;
                end
                else begin
                    tot_acc_id_buffer[i] <= tot_acc_id_buffer[i - 1];
                    src_p_valid_buffer[i] <= src_p_valid_buffer[i - 1];
                end
            end
        end
    endgenerate

    assign src_p_valid = src_p_valid_buffer[DIV_DELAY - 1];
    assign tot_acc_id = tot_acc_id_buffer[DIV_DELAY - 1];

    always @ (posedge clk) begin
        if (rst) begin
            tot_acc_id_buffer[0] <= 0;
            src_p_valid_buffer[0] <= 0;
        end
        else begin
            tot_acc_id_buffer[0] <= front_tot_acc_id;
            src_p_valid_buffer[0] <= front_src_p_valid;
        end
    end

endmodule

module edge_process_vertex_single #(parameter
    DST_ID_DWIDTH       = `DST_ID_DWIDTH,
    VERTEX_MASK_WIDTH   = `VERTEX_MASK_WIDTH,
    DIV_DELAY           = `DIV_DELAY
    ) (
        input                                   clk,
        input                                   rst,
        input                                   front_data_valid,
        input [DST_ID_DWIDTH - 1 : 0]           front_dst_id,
        input [VERTEX_MASK_WIDTH - 1 : 0]       front_src_p_mask_r,
        input                                   front_dst_data_valid,

        output [DST_ID_DWIDTH - 1 : 0]          dst_id,
        output [VERTEX_MASK_WIDTH - 1 : 0]      src_p_mask_r,
        output                                  dst_data_valid);

    reg [DST_ID_DWIDTH - 1 : 0] dst_id_buffer [0 : DIV_DELAY - 1];
    reg [VERTEX_MASK_WIDTH - 1 : 0] src_p_mask_r_buffer [0 : DIV_DELAY - 1];
    reg [DIV_DELAY - 1 : 0] dst_data_valid_buffer;

    assign dst_id = dst_id_buffer[DIV_DELAY - 1];
    assign src_p_mask_r = src_p_mask_r_buffer[DIV_DELAY - 1];
    assign dst_data_valid = dst_data_valid_buffer[DIV_DELAY - 1];

    always @ (posedge clk) begin
        if (rst) begin
            dst_id_buffer[0]          <= 0;
            src_p_mask_r_buffer[0]    <= 0;
            dst_data_valid_buffer[0]  <= 0;
        end
        else begin
            if (front_dst_data_valid && front_data_valid) begin
                dst_id_buffer[0]          <= front_dst_id;
                src_p_mask_r_buffer[0]    <= front_src_p_mask_r;
                dst_data_valid_buffer[0]  <= front_dst_data_valid;
            end
            else begin
                dst_id_buffer[0]          <= 0;
                src_p_mask_r_buffer[0]    <= 0;
                dst_data_valid_buffer[0]  <= 1'b0;
            end
        end
    end

    generate
        genvar i;
        for (i = 1; i < DIV_DELAY; i = i + 1) begin : M13_BLOCK_4
            always @ (posedge clk) begin
                if (rst) begin
                    dst_id_buffer[i] <= 0;
                    src_p_mask_r_buffer[i] <= 0;
                    dst_data_valid_buffer[i] <= 0;
                end
                else begin
                    dst_id_buffer[i] <= dst_id_buffer[i - 1];
                    src_p_mask_r_buffer[i] <= src_p_mask_r_buffer[i - 1];
                    dst_data_valid_buffer[i] <= dst_data_valid_buffer[i - 1];
                end
            end
        end
    endgenerate

endmodule